Skip to content

Comments

fix: prevent race condition in step content persistence#2796

Open
hztBUAA wants to merge 1 commit intoChainlit:mainfrom
hztBUAA:fix/step-race-condition
Open

fix: prevent race condition in step content persistence#2796
hztBUAA wants to merge 1 commit intoChainlit:mainfrom
hztBUAA:fix/step-race-condition

Conversation

@hztBUAA
Copy link
Contributor

@hztBUAA hztBUAA commented Feb 20, 2026

Summary

Fixes #2789

Race condition in ChainlitDataLayer causes Steps to lose content when the initial step.send() (with empty output) finishes after the final step.update() (with real output). The COALESCE in the upsert SQL treats empty string as a valid value, so it overwrites the actual content instead of preserving it.

Root cause

  1. cl.Step initializes input and output as empty strings ("")
  2. The create_step SQL uses COALESCE(EXCLUDED.output, "Step".output) to preserve existing values
  3. COALESCE only skips NULL, not empty strings — so an empty string from the initial send() overwrites real content written by update()

Fix

Convert empty string input/output to NULL before passing to the database query. This allows the existing COALESCE logic to correctly preserve content when a late-arriving send() would otherwise overwrite it.

# Before
"input": step_dict.get("input"),
"output": step_dict.get("output"),

# After
"input": step_dict.get("input") or None,
"output": step_dict.get("output") or None,

Test plan

  • All existing step tests pass (45/45)
  • All existing data layer tests pass (35/35, excluding unrelated missing optional deps)
  • Empty string input/output is now treated as NULL in DB, so COALESCE correctly falls back to existing content
  • Non-empty input/output values are persisted normally (truthy strings pass through or None unchanged)

Summary by cubic

Prevents step content loss when send() (empty output) finishes after update() (real output). Empty input/output are now stored as NULL so the upsert COALESCE preserves existing content.

  • Bug Fixes
    • Convert empty input/output to NULL before DB upsert in ChainlitDataLayer.create_step.
    • Stops late send() from overwriting real output; non-empty values persist normally.

Written for commit 4544aa8. Summary will update on new commits.

Convert empty string input/output to NULL before persisting to database,
so that the existing COALESCE logic in the upsert query correctly
preserves content when the initial send() (with empty output) commits
after update() (with real output).

Fixes Chainlit#2789
@dosubot dosubot bot added size:XS This PR changes 0-9 lines, ignoring generated files. bug Something isn't working data layer Pertains to data layers. labels Feb 20, 2026
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 1 file

"thread_id": step_dict.get("threadId"),
"parent_id": step_dict.get("parentId"),
"input": step_dict.get("input"),
"input": step_dict.get("input") or None,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems this would be a more elegant approach.

Suggested change
"input": step_dict.get("input") or None,
"input": step_dict.get("input", None)

@dokterbob
Copy link
Collaborator

@hztBUAA Would be GREAT to have a regression (unit) test demonstrating the issue on main (e.g. the test fails), then passing on your PR!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working data layer Pertains to data layers. size:XS This PR changes 0-9 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Race condition in ChainlitDataLayer causes Steps to lose content in History

2 participants